﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SIEGate.Core;
using SIEGate.Core.Measurements;
using SIEGate.Core.Measurements.Values;
using SIEGate.Core.MessageBusses;
using SIEGate.Core.Strawman;

namespace SIEGateCoreTest.DataProcessorTests
{
    [TestClass]
    public class TestNaiveTcpNetworkCommunicator
    {
        // Test currently fails due to UnPublish not being implemented, so that's an acceptable failure.
        [TestMethod]
        public void TestAddTcpConnection()
        {
            IPAddress ipAddress = IPAddress.Loopback;
            int port = 7999;
            IPEndPoint localIpEndPoint = new IPEndPoint(ipAddress, port);

            ChannelMessageBus localBus = new ChannelMessageBus();

            // Shape:
            // [ incomingStreamId ] -> Network Communicator --outgoingStreamId--> network --outgoingStreamId --> Same Network Communicator -> [ outgoingStreamId ]
            // Network Communicator needs to be subscribed to incomingStreamId and publish to outgoingStreamId

            NetworkCommunicator.NetworkCommunicatorInfoBase localInfo = new NetworkCommunicator.NetworkCommunicatorInfoBase();

            // This simulates something pushing onto the data bus from somewhere else.
            Guid incomingStreamId = Guid.NewGuid();
            HashSet<Guid> guidsGoingIntoNetworkCommunicator = new HashSet<Guid>();
            guidsGoingIntoNetworkCommunicator.Add(incomingStreamId);
            localInfo.IncomingStreamIDs.Add(incomingStreamId);

            IMessageBusProxy sendingProxy = localBus.GetProxy("fromBus");
            sendingProxy.Publish(guidsGoingIntoNetworkCommunicator);

            NaiveSocketNetworkCommunicator receivingComms = new NaiveSocketNetworkCommunicator(localIpEndPoint, localBus.GetProxy("receivingComms"), localInfo);
            receivingComms.IsListener = true;
            receivingComms.Start();

            Guid outgoingStreamId = Guid.NewGuid();
            HashSet<Guid> guidsComingFromNetworkCommunicator = new HashSet<Guid>();
            guidsComingFromNetworkCommunicator.Add(outgoingStreamId);
            localInfo.OutgoingStreamIDs.Add(outgoingStreamId);

            // This is a very unusual use of comms; normally there would be no reason to involve the network
            // But for the test's sake the only system we know we have access to is the one the test is running
            // on.  Since NaiveSocketNetworkCommunicator always listens on one port and transmits over that
            // port, we can only have the one.
            NaiveSocketNetworkCommunicator sendingComms = new NaiveSocketNetworkCommunicator(localIpEndPoint, localBus.GetProxy("sendingComms"), localInfo);
            sendingComms.IsListener = false;

            sendingComms.Start();

            sendingComms.Connect(ipAddress, port);

            // Used to determine if data successfully fully crosses the network
            IMessageBusProxy receivingProxy = localBus.GetProxy("receive");
            receivingProxy.Subscribe(guidsComingFromNetworkCommunicator);

            StringValue stringValue = new StringValue("TestAddSocketRawData String Data");
            sendingProxy.Send(new RawData(incomingStreamId, stringValue));

            // Give the data time to be sent & received
            System.Threading.Thread.Sleep(1000);
            Stopwatch s = new Stopwatch();
            s.Start();

            IMeasurement[] received = null;
            MeasurementListValue receivedList = null;
            IMeasurement[] rawList = null;
            while (s.Elapsed < new TimeSpan(0, 1, 0)
                   && ((received == null)
                        || (received.Length == 0)
                        || (receivedList == null)
                        || (rawList == null)
                        || (rawList.Length == 0)))
            {
                received = receivingProxy.Recv(2);
                if ((received != null) && (received.Length > 0))
                {
                    receivedList = received[0].Value as MeasurementListValue;
                    rawList = receivedList.GetRaw() as IMeasurement[];
                }
            }
            Assert.IsFalse(received == null);
            if (received != null)
            {
                Assert.IsTrue(received.Length > 0);
                Assert.IsFalse(receivedList == null);
                Assert.IsFalse(rawList == null);
                if (rawList != null)
                {
                    Assert.IsTrue(rawList.Length > 0);
                    StringValue receivedStringValue = (rawList[0].Value as StringValue);
                    Assert.IsTrue(receivedStringValue != null);
                    if (receivedStringValue != null)
                    {
                        Assert.AreEqual(stringValue.ToString(), receivedStringValue.ToString());
                    }
                }
            }

            // Throws exception due to UnPublish not currently being implemented.
            sendingComms.Stop();
        }
    }
}
